﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace Walson.Windows.Forms
{
    [DefaultEvent("ValueChanged")]
    public partial class LinearTuner : Control
    {

        private decimal _Value;
        public decimal Value
        {
            get
            {
                return _Value;
            }
            set
            {
                while (value < _Min)
                    value = _Max - (_Min - value);
                while (value > _Max)
                    value = _Min + value - _Max;
                if (value == _Max)
                    value = _Min;
                _Value = value;
                RaiseValueChangedEvent();
                this.Invalidate();
            }
        }

        private int _Max = 100;
        public int Max
        {
            get
            {
                return _Max;
            }
            set
            {
                _Max = value;
                this.Invalidate();
            }
        }

        private int _Min = 0;
        public int Min
        {
            get
            {
                return _Min;
            }
            set
            {
                _Min = value;
                this.Invalidate();
            }
        }

        private TunerStyle _Style = TunerStyle.Horizontal;
        public TunerStyle Style
        {
            get
            {
                return _Style;
            }
            set
            {
                _Style = value;
                this.Invalidate();
            }
        }

        public enum TunerStyle
        {
            Vertical,
            Horizontal
        }

        private const float _gap = 60;

        private Pen _WhitePen;
        private Pen _RedPen;
        private Pen _PinkPen;
        private bool _Dragging = false;
        private Point _DragPoint;
        private float _OldValue;

        public LinearTuner()
        {
            InitializeComponent();
            this.DoubleBuffered = true;

            _WhitePen = new Pen(Brushes.White, 2);
            _RedPen = new Pen(Brushes.Red, 4);
            //_PinkPen = new Pen(Color.FromArgb(239, 123, 247), 2);
            _PinkPen = new Pen(Color.FromArgb(255, 125, 255), 2);

            this.MouseMove += new MouseEventHandler(NumericIndicator_MouseMove);
            this.MouseDown += new MouseEventHandler(NumericIndicator_MouseDown);
            this.MouseUp += new MouseEventHandler(NumericIndicator_MouseUp);
        }

        void NumericIndicator_MouseUp(object sender, MouseEventArgs e)
        {
            //throw new NotImplementedException();
            _Dragging = false;
        }

        void NumericIndicator_MouseDown(object sender, MouseEventArgs e)
        {
            //throw new NotImplementedException();
            _Dragging = true;
            _DragPoint = e.Location;
            _OldValue =(float) _Value;
        }

        void NumericIndicator_MouseMove(object sender, MouseEventArgs e)
        {
            if (_Dragging == false)
            {
                return;
            }

            if (this._Style == TunerStyle.Horizontal)
            {
                this.Value =(decimal) (_OldValue - (e.Location.X - _DragPoint.X) / _gap);
            }
            else
            {
                this.Value = (decimal)(_OldValue - (e.Location.Y - _DragPoint.Y) / _gap);
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            using (Bitmap bmp = new Bitmap(this.Width, this.Height))
            {
                using (Graphics g = Graphics.FromImage(bmp))
                {
                    Font font = new Font("Times NewRoman", 10f);

                    g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

                    g.Clear(Color.FromArgb(57, 49, 66));

                    RectangleF rec1 = new RectangleF(0, 0, this.Width * 2f / 3f, this.Height);
                    RectangleF rec2 = new RectangleF(this.Width * 2f / 3f-1, 0f, this.Width / 3f, this.Height);
                    LinearGradientBrush lb1 = new LinearGradientBrush(rec1, Color.FromArgb(57, 49, 66), Color.Gray, 0f);
                    LinearGradientBrush lb2 = new LinearGradientBrush(rec2, Color.Gray, Color.FromArgb(57, 49, 66), 0f);
                    g.FillRectangle(lb1, rec1);
                    g.FillRectangle(lb2, rec2);
                    lb1.Dispose();
                    lb2.Dispose();

                    PointF[] polygon = new PointF[3];
                    polygon[0] = new PointF(this.Width / 2, 7);
                    polygon[1] = new PointF(this.Width / 2 - 5, 2);
                    polygon[2] = new PointF(this.Width / 2 + 5, 2);

                    g.FillPolygon(Brushes.White, polygon);

                    float offset = (float)(_Value - (int)_Value);
                    float start = this.Width / 2 - offset * _gap;
                    PointF[] line = new PointF[2];
                    float x;
                    for (int i = 0; i < this.Width / (_gap / 2); i++)
                    {
                        x = i * _gap / 2;
                        line[0] = new PointF(start +  i * _gap / 2, 10);
                        line[1] = new PointF(start +  i * _gap / 2, 17);
                        g.DrawLine(_WhitePen, line[0], line[1]);

                        line[0] = new PointF(start -  i * _gap / 2, 10);
                        line[1] = new PointF(start -  i * _gap / 2, 17);
                        g.DrawLine(_WhitePen, line[0], line[1]);

                        for (int j = 0; j < 5; j++)
                        {
                            line[0] = new PointF(start + i * _gap / 2 + j * _gap / 5, 10);
                            line[1] = new PointF(start + i * _gap / 2 + j * _gap / 5, 15);
                            g.DrawLine(Pens.WhiteSmoke, line[0], line[1]);

                            line[0] = new PointF(start - i * _gap / 2 + j * _gap / 5, 10);
                            line[1] = new PointF(start - i * _gap / 2 + j * _gap / 5, 15);
                            g.DrawLine(Pens.WhiteSmoke, line[0], line[1]);
                        }

                            if (i % 2 == 0)
                            {
                                int h;
                                float len;

                                if (_Value >= 0)
                                    h = (int)Math.Floor(_Value + i / 2);
                                else
                                    h = (int)Math.Ceiling(_Value + i / 2);


                                while (h < _Min)
                                    h = _Max - (_Min - h);
                                while (h > _Max)
                                    h = _Min + h - _Max;
                                if (h == _Max)
                                    h = _Min;

                                //                             if (h >= 360)
                                //                                 h -= 360;
                                len = g.MeasureString(h.ToString(), font).Width;
                                g.DrawString(h.ToString(), font, Brushes.White, start + i * _gap / 2 - len / 2, 19);

                                if (_Value >= 0)
                                    h = (int)Math.Floor(_Value - i / 2);
                                else
                                    h = (int)Math.Ceiling(_Value - i / 2);

                                while (h < _Min)
                                    h = _Max - (_Min - h);
                                while (h > _Max)
                                    h = _Min + h - _Max;
                                if (h == _Max)
                                    h = _Min;
                                //                             if (h < 0)
                                //                                 h += 360;
                                len = g.MeasureString(h.ToString(), font).Width;
                                g.DrawString(h.ToString(), font, Brushes.White, start - i * _gap / 2 - len / 2, 19);
                            }
                    }

                    g.ResetTransform();
                    line = new PointF[3];
                    line[0] = new PointF(this.Width - 1, 0);
                    line[1] = new PointF(0, 0);
                    line[2] = new PointF(0, this.Height);
                    g.DrawLines(Pens.Black, line);

                    line[0] = new PointF(0, this.Height - 1);
                    line[1] = new PointF(this.Width - 1, this.Height-1);
                    line[2] = new PointF(this.Width - 1, 0);
                    g.DrawLines(Pens.WhiteSmoke, line);
                }
                e.Graphics.DrawImage(bmp, 0, 0);
            }
            base.OnPaint(e);
        }

        /// <summary>
        /// 值改变事件处理方法
        /// </summary>
        /// <param name="sender">事件发送者</param>
        public delegate void ValueChangedHandler(object sender);
        /// <summary>
        /// 值改变事件
        /// </summary>
        public event ValueChangedHandler ValueChanged;
        /// <summary>
        /// 触发值改变事件
        /// </summary>
        protected void RaiseValueChangedEvent()
        {
            if (this.ValueChanged != null)
                ValueChanged((object)this);
        }
    }
}
